import 'dart:async';

import 'package:built_collection/built_collection.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:mobile_app.core/core/domain/config/config.dart';
import 'package:mobile_app.core/core/domain/entities/access_credentials.dart';
import 'package:mobile_app.core/core/domain/entities/google_apple_store_api/purchased_item/purchased_item.dart';
import 'package:mobile_app.core/core/domain/entities/premium_access_info.dart';
import 'package:mobile_app.core/core/domain/entities/premium_access_status.dart';
import 'package:mobile_app.core/core/domain/entities/purchased_item/purchased_item.dart';
import 'package:mobile_app.core/core/domain/entities/user.dart';
import 'package:mobile_app.core/core/domain/helpers/create_premium_access_status/helper.dart';
import 'package:mobile_app.core/core/domain/helpers/get_premium_access_info_from_purchased_items/helper.dart';
import 'package:mobile_app.core/core/domain/helpers/handle_access_credential_update/helper.dart';
import 'package:mobile_app.core/core/domain/repositories/purchased_item_api_repository/repository.dart'
    as repo;
import 'package:mobile_app.core/core/domain/repositories/user_repository/repository.dart';
import 'package:mobile_app.core/core/domain/repositories/user_secure_repository/repository.dart';
import 'package:mobile_app.core/core/domain/services/analytics_service/service.dart';
import 'package:mobile_app.core/core/domain/services/google_apple_store_api_service/service.dart';
import 'package:mobile_app.core/core/domain/utils/either_extension.dart';

part 'failures.dart';

part 'result.dart';

abstract class ObtainPurchasedItems {
  Future<Either<PurchasedItemObtainmentFailure, PurchasedItemObtainmentResult>> call({
    required String deviceId,
    required DateTime? demoExpirationTime,
    required bool introFinished,
  });
}

class ObtainPurchasedItemsImpl implements ObtainPurchasedItems {

  const ObtainPurchasedItemsImpl({
    required CreatePremiumAccessStatus createPremiumAccessStatus,
    required HandleAccessCredentialUpdate handleAccessCredentialUpdate,
    required GetPremiumAccessInfoFromPurchasedItems getPremiumAccessInfoFromPurchasedItems,
    required GoogleAppleStoreApiService googleAppleStoreApiService,
    required repo.PurchasedItemApiRepository purchasedItemApiRepository,
    required UserRepository userRepository,
    required UserSecureRepository userSecureRepository,
    required AnalyticsService analyticsService,
  })  : _createPremiumAccessStatus = createPremiumAccessStatus,
        _handleAccessCredentialUpdate = handleAccessCredentialUpdate,
        _getPremiumAccessInfoFromPurchasedItems = getPremiumAccessInfoFromPurchasedItems,
        _googleAppleStoreApiService = googleAppleStoreApiService,
        _purchasedItemApiRepository = purchasedItemApiRepository,
        _userRepository = userRepository,
        _userSecureRepository = userSecureRepository,
        _analyticsService = analyticsService;

  final CreatePremiumAccessStatus _createPremiumAccessStatus;
  final HandleAccessCredentialUpdate _handleAccessCredentialUpdate;
  final GetPremiumAccessInfoFromPurchasedItems _getPremiumAccessInfoFromPurchasedItems;

  final GoogleAppleStoreApiService _googleAppleStoreApiService;
  final repo.PurchasedItemApiRepository _purchasedItemApiRepository;
  final UserRepository _userRepository;
  final UserSecureRepository _userSecureRepository;

  final AnalyticsService _analyticsService;

  @override
  Future<Either<PurchasedItemObtainmentFailure, PurchasedItemObtainmentResult>> call({
    required String deviceId,
    required DateTime? demoExpirationTime,
    required bool introFinished,
  }) async {
    String? appStoreReceipt;
    BuiltList<GooglePlayPurchasedItemIdentifier>? googlePlayPurchasedItemIdentifiers;

    switch (Config().applicationStore) {
      case ApplicationStore.appStore:
        final failureOrStoreApiPurchasedItems =
            await _googleAppleStoreApiService.getPurchasedItems();

        if (failureOrStoreApiPurchasedItems.isLeft()) {
          const failure = NetworkPurchasedItemObtainmentFailure();
          return left(failure);
        }

        final storeApiPurchasedItems = failureOrStoreApiPurchasedItems.asRight();

        if (storeApiPurchasedItems.isNotEmpty) {
          appStoreReceipt = storeApiPurchasedItems.last.transactionReceipt;
        }

      case ApplicationStore.googlePlay:
        final failureOrStoreApiPurchasedItems =
            await _googleAppleStoreApiService.getPurchasedItems();

        if (failureOrStoreApiPurchasedItems.isLeft()) {
          const failure = NetworkPurchasedItemObtainmentFailure();
          return left(failure);
        }

        final storeApiPurchasedItems = failureOrStoreApiPurchasedItems.asRight();

        googlePlayPurchasedItemIdentifiers = storeApiPurchasedItems.map((storeApiPurchasedItem) {
          final googlePlayStoreApiPurchasedItem =
              storeApiPurchasedItem as GooglePlayApiPurchasedItem;

          return GooglePlayPurchasedItemIdentifier(
            purchaseToken: googlePlayStoreApiPurchasedItem.purchaseToken,
            purchaseItemSku: googlePlayStoreApiPurchasedItem.purchaseItemSku,
          );
        }).toBuiltList();
    }

    final repositoryPurchasedItemObtainmentFailureOrResult =
        await _purchasedItemApiRepository.obtainPurchasedItems(
      deviceId: deviceId,
      appStoreReceipt: appStoreReceipt,
      googlePlayPurchasedItemIdentifiers: googlePlayPurchasedItemIdentifiers,
    );

    if (repositoryPurchasedItemObtainmentFailureOrResult.isLeft()) {
      final repositoryFailure = repositoryPurchasedItemObtainmentFailureOrResult.asLeft();

      switch (repositoryFailure) {
        case repo.ServerPurchasedItemObtainmentFailure():
          const failure = ServerPurchasedItemObtainmentFailure();
          return left(failure);

        case repo.NetworkPurchasedItemObtainmentFailure():
          const failure = NetworkPurchasedItemObtainmentFailure();
          return left(failure);

        case repo.UnauthorizedPurchasedItemObtainmentFailure():
          const updatedUser = null;
          const updatedAccessCredentials = null;

          _handleAccessCredentialUpdate(
            accessCredentials: updatedAccessCredentials,
          );

          unawaited(_userRepository.saveUser(
            user: updatedUser,
          ));

          if (Config().analyticsCollectionEnabled) {
            _analyticsService.setUserId(null);
          }

          const failure = UnauthorizedPurchasedItemObtainmentFailure(
            user: updatedUser,
            accessCredentials: updatedAccessCredentials,
          );

          return left(failure);
      }
    }

    final repositoryPurchasedItemObtainmentResult =
        repositoryPurchasedItemObtainmentFailureOrResult.asRight();

    final premiumAccessInfo = _getPremiumAccessInfoFromPurchasedItems(
      demoExpirationTime: demoExpirationTime,
      appStorePurchasedItems: repositoryPurchasedItemObtainmentResult.appStorePurchasedItems,
      googlePlayPurchasedItems: repositoryPurchasedItemObtainmentResult.googlePlayPurchasedItems,
      webPurchasedItems: repositoryPurchasedItemObtainmentResult.webPurchasedItems,
    );

    final premiumAccessStatus = _createPremiumAccessStatus(
      introFinished: introFinished,
      premiumAccessInfo: premiumAccessInfo,
    );

    unawaited(_userSecureRepository.savePremiumAccessInfo(
      premiumAccessInfo: premiumAccessInfo,
    ));

    final result = PurchasedItemObtainmentResult(
      premiumAccessInfo: premiumAccessInfo,
      premiumAccessStatus: premiumAccessStatus,
    );

    return right(result);
  }
}
